/** Fichier implémentant le "cube troué".
 *
 * Ce fichier contient l'implémentation de la classe CubeTroue.
 *
 * @file CubeTroue.cpp
 * @author Johan "Solo" Luisier
 *
 * @date 2010
 */
#include "CubeTroue.hpp"

#include "Algebre/Solveur.hpp"
#include "Algebre/ConstruitMatrice.hpp"

#include "Utils/Erreur.hpp"
#include "Utils/StatusCode.hpp"
#include "Utils/debugstreams.hpp"

#include <cmath>
#include <cstdlib>

#include <iostream>

using namespace std;

/** Constructeur standard.
 *
 * Ce constructeur permet d'initialiser tous les champs du CubeTroue. La
 * transformaion est initialisée ici et peut causer un arrêt du programme en
 * cas d'échec répété.
 *
 * On ne donne au constructeur que 2 arrêtes, la troisième étant obtenue par
 * produit vectoriel. On vérifie tout d'abord que les deux vecteur donnés sont
 * bien orthogonaux.
 *
 * Une exception de type Erreur est lancée si la Transformation ne
 * peut être initialisée.
 *
 * @throw Erreur si l'instance de Transformation ne peut être initialisée.
 *
 * @param[in] pt un des sommet du CubeTroue.
 * @param[in] v1 donne la direction d'une des arrêtes.
 * @param[in] v2 donne la direction d'une des arrêtes.
 * @param[in] ratio rapport d'homothécie.
 */
CubeTroue::CubeTroue(const Coord& pt, const VecteurU& v1, const VecteurU& v2,
                     const double& ratio)
    : Forme( "CubeTroue" ), Coin( pt )//, Homothetie( ratio )
{
    MatriceSO3 test;

    StatusCode sc( axesXY( v1, v2, test ) );

    if ( ! sc )
    {
        error() << sc.message() << endreq;
        exit( 1 );
    }
    else
    {
        VersGlobal = Transformation( test, Coin, ratio / 3, false );
        VersLocal  = VersGlobal.inverse();
    }

    MoteurResolution = new Solveur();

    PseudoCentre = VersGlobal( Coord( 0., 0., 0. ) );

    PseudoRayon = VersGlobal( 4. );
}

/**
 * Constructeur de copie, permettant la duplication d'un CubeTroue.
 *
 * @param[in] cube objet à copier.
 */
CubeTroue::CubeTroue(const CubeTroue& cube)
    : Forme( "CubeTroue", cube.VersGlobal ), Coin( cube.Coin )
{
    MoteurResolution = new Solveur();

    PseudoCentre = VersGlobal( Coord( .5, .5, .5 ) );

    PseudoRayon = VersGlobal( sqrt( 8 ) );
}

/**
 * Le destructeur doit libérer la mémoire allouée pour le Solveur.
 */
CubeTroue::~CubeTroue()
{}

/**
 * Il faut d'abord transformer le rayon dans le système de coordonnées
 * local, ce qui nécessite une translation, une rotation et une
 * homothétie. Ensuite on fait appel au Solveur pour résoudre
 * l'intersection.
 */
Coord CubeTroue::trouveIntersection(Rayon const & ray) const
{
    VecteurU dir( ray.direction() );
    Vecteur origine;

    dir     = VersLocal( dir );
    origine = VersLocal( ray.origine() );

    double a( pow( dir.x(), 4 ) + pow( dir.y(), 4 ) + pow( dir.z(), 4 ) );
    double b( 4. * ( pow( dir.x(), 3 ) * origine.x() +
                     pow( dir.y(), 3 ) * origine.y() +
                     pow( dir.z(), 3 ) * origine.z() ) );
    double c( 6. * ( pow( dir.x(), 2 ) * pow( origine.x(), 2 ) +
                     pow( dir.y(), 2 ) * pow( origine.y(), 2 ) +
                     pow( dir.z(), 2 ) * pow( origine.z(), 2 ) )
              - 5. * dir.norme() );
    double d( 4. * ( pow( origine.x(), 3 ) * dir.x() +
                     pow( origine.y(), 3 ) * dir.y() +
                     pow( origine.z(), 3 ) * dir.z() )
              - 10. * ( dir * origine ) );
    double e(  pow( origine.x(), 4 ) + pow( origine.y(), 4 ) +
               pow( origine.z(), 4 ) - 5. * ( origine.norme() )  + 11.8 );

    MoteurResolution -> setParams( e, d, c, b, a );

    vector< double > solutions;

    MoteurResolution -> resoud( solutions );

    vector< double >::const_iterator It, Begin( solutions.begin() ),
            End( solutions.end() );

    // for (It = Begin; It != End; It++)
    //   cout << *It << " ";
    // cout << endl;
    for (It = Begin; It != End; It++)
    {
        if ( *It > 1e-7 )
        {
            Vecteur local( origine );
            local += (*It) * dir ;
            // cout << "En coordonnées locales : " << local << endl;
            // cout << Transformation.inverse() << endl;
            local = VersGlobal( static_cast<Coord>( local ) );
            return static_cast<Coord>( local );
        }
    }

    return NullePart;
}

VecteurU CubeTroue::normale(Coord const & coord) const
{
    Vecteur pt( VersLocal( coord ) );

    Vecteur local( 4. * pow( pt.x(), 3 ) - 10. * pt.x(),
                   4. * pow( pt.y(), 3 ) - 10. * pt.y(),
                   4. * pow( pt.z(), 3 ) - 10. * pt.z() );

    return VecteurU( local );
}

/**
 * @todo Implémenter cette méthode, qui retourne @e false tout le
 * temps pour l'instant.
 */
bool CubeTroue::estDans(Coord const &) const
{
    return false;
}

bool CubeTroue::estSur(Coord const& coord) const
{
    Vecteur pt( VersLocal( static_cast< Vecteur >( coord ) ) );

    return ( abs( pow( pt.x(), 4 ) - 5. * pow( pt.x(), 2 ) +
                  pow( pt.x(), 4 ) - 5. * pow( pt.x(), 2 ) +
                  pow( pt.x(), 4 ) - 5. * pow( pt.x(), 2 ) + 11.8 ) < 1e-7 );
}

/**
 * Pas de mapping implémenté ici.
 */
void CubeTroue::mapping(const Coord&, int&, int&, const int&,
                        const int&, const int&) const
{}

/**
 * Affiche Coin et Homothetie.
 */
ostream& CubeTroue::affiche(ostream& os) const
{
    os << "Forme : " << nom() << endl << "Paramètres :" << endl
       << "\tCoin    : " << Coin << endl;
    os << "Volume centré en " << PseudoCentre << ", rayon = "
       << PseudoRayon << endl;


    return os;
}

Coord CubeTroue::intrinseque(const Coord& pt) const
{
    return Coord();
}
